#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include "mem_pool.h"

pool_t * create_pool(size_t size){
    unsigned int space_len = size + sizeof(pool_t);
    pool_t *pool = malloc(space_len);
    if(!pool)
        return NULL;

    pool->d.end   = (char*)pool + space_len;
    pool->d.last  = (char*)pool + sizeof(pool_t);
    pool->d.next  = NULL;
    pool->d.failed = 0;

    pool->max     = size;
    pool->current = pool;
    pool->large   = NULL;
    pool->cleanup = NULL;
    pool->recycle = NULL;

    return pool;
}

void  destroy_pool(pool_t *pool){
    pool_cleanup_t *c;
    pool_large_t   *l;
    pool_t         *p,*n;

    if(!pool) return;

    for(c = pool->cleanup; c ; c = c->next){
        if(c->handler){
            printf("pool(%p) destroy cleanup(%p) c->data(%p)\n",pool,c,c->data);
            c->handler(c->data);
        }
    }

    for(l = pool->large; l ; l = l->next){
        if(l->alloc){
            printf("pool(%p) destroy large(%p) large->alloc(%p)\n",pool,l,l->alloc);
            free(l->alloc);
            l->alloc = NULL;
        }
    }

    for(p = pool , n = pool->d.next ;  ; p = n , n = n->d.next ){
        printf("pool(%p) destroyed )\n",p);
        free(p);
        if(n == NULL)
            break;
    }
}

void reset_pool(pool_t *pool){
    pool_large_t   *l;
    pool_t         *p;

    if(!pool) return ;

    for(l = pool->large; l ; l = l->next){
        if(l->alloc){
            printf("pool(%p) destroy large(%p) large->alloc(%p)\n",pool,l,l->alloc);
            free(l->alloc);
            l->alloc = NULL;
        }
    }
    pool->large = NULL;

    for(p = pool; p ; p = p->d.next){
        printf("pool(%p) reset )\n",p);
        p->d.last = (char*)p + sizeof(pool_t);
        p->d.failed = 0;
        p->recycle  = NULL;
    }
}

void * pool_palloc_from_recycle(pool_t *pool,size_t size)
{
    pool_t *p;

    for(p=pool ; p ; p = p->d.next){
        pool_recycle_t *cur = p->recycle, *prev = cur;
        for( ; cur ; prev = cur , cur = cur->next){
            if(cur->len >= size){
                printf("pool_palloc_from_recycle %p len:%ld size:%ld next:%p\n",cur,cur->len,size,cur->next);
                if(cur == p->recycle){
                    p->recycle = cur->next;
                }else{
                    prev->next = cur->next;
                }
                return (void*)cur;
            }
        }
    }

    return NULL;
}

void * pool_palloc(pool_t *pool,size_t size){
    if(size < PALLOC_SMALLEST_SIZE) size = PALLOC_SMALLEST_SIZE;
    printf("pool_palloc size:%ld max:%ld\n",size,pool->max);

    if(size <= pool->max){
        pool_t *p;
        void *m = pool_palloc_from_recycle(pool,size);
        if(m) return m;
        p = pool->current;
        do{
             if(size <= (unsigned int)(p->d.end - p->d.last)){
                m = p->d.last;
                p->d.last += size;
                return m;
            }
            p = p->d.next;
        }while(p);
        return pool_palloc_block(pool,size);
    }

    return pool_palloc_large(pool,size);
}

void * pool_calloc(pool_t *pool,size_t size){
    void *m = pool_palloc(pool,size);
    if(m)
        memset(m,0,size);

    return m;
}

void * pool_palloc_block(pool_t *pool,size_t size){
    void    *m;
    pool_t  *new_one,*p,*current;

    unsigned int psize = pool->d.end - (char*)pool;
    new_one = create_pool(psize);
    if(!new_one)
        return NULL;

    m = new_one->d.last;
    new_one->d.last += size;

    current = pool->current;
    for(p = current ; p->d.next ; p = p->d.next){
        if(p->d.failed++ > 4){
            current = p->d.next;
        }
    }

    pool->d.next = new_one;
    pool->current = current ? current : new_one;

    return m;
}

void * pool_palloc_large(pool_t *pool,size_t size){
    void         *m;
    pool_large_t *large;
    int cnt;

    m = malloc(size);
    if(!m)
        return NULL;

    cnt = 0;
    printf("large: begin pool->large:%p last:%p\n",pool->large,pool->d.last);
    for(large = pool->large; large ; large = large->next){
        if(large->alloc == NULL){
            printf("large:%p  next:%p\n",large,large->next);
            large->alloc = m;
            return m;
        }
        if(cnt++ > 3)
            break;
    }

    large = pool_calloc(pool,sizeof(pool_large_t));
    if(!large){
        free(m);
        printf("pool_palloc_large error!\n");
        return NULL;
    }

    large->alloc = m;
    large->next  = pool->large;
    pool->large  = large;
    printf("large:%p %p  last:%p end\n",large,large->next,pool->d.last);

    return m;
}

void * pool_realloc(pool_t *pool,void *data,size_t oldsize,size_t size){
    pool_t *p;
    pool_large_t *large;
    void *m;

    if(!pool ) return NULL;

    if (oldsize >= size)
        return data;

    if(data == NULL){
        m = pool_calloc(pool,size);
        printf("pool_realloc first alloc:%p(%ld:%ld)\n",m,size,pool->max);
        return m;
    }
    if(oldsize > pool->max){
        void *temp;
        for(large = pool->large; large ; large = large->next){
            if(large->alloc == data)
                break;
        }
        if(large==NULL) return NULL;
        temp = realloc(data,size);
        if (!temp)
            return NULL;
        large->alloc = temp;
        return large->alloc;
    }
    for(p=pool; p ; p = p->d.next){
        if( (char*)data >= ((char*)p+sizeof(pool_t)) &&
            ((char*)data+oldsize) <= p->d.end)
            break;
    }
    if(p==NULL) return NULL;
    if( ((char*)data+oldsize) == p->d.last && (size-oldsize) <= (unsigned int)(p->d.end-p->d.last)){
        p->d.last += (size-oldsize);
        return data;
    }else{
        m = pool_calloc(pool,size);
        if(!m) return NULL;
        memcpy(m,data,oldsize);
        pool_recycle(p,data,oldsize);
        return m;
    }
}

unsigned int pool_recycle(pool_t *pool,void *pdata,size_t len){
    pool_t *p;
    pool_recycle_t *this;

    if(!pool || !pdata || !len ) return 1;

    printf("pool_recycle %p %ld\n",pdata,len);

    if(len < PALLOC_SMALLEST_SIZE) len = PALLOC_SMALLEST_SIZE;

    for( p=pool ; p ; p = p->d.next){
        if( (char*)pdata >= ((char*)p + sizeof(pool_t)) &&
            ((char*)pdata+len) <= p->d.end)
            break;
    }
    if(p == NULL) return 1;

    //memset(pdata,0,len);
    //this = (pool_recycle_t*)pdata;
    this = pdata;
    this->len  = len;
    this->next = NULL;
    if(!p->recycle){
        p->recycle = this;
        return 0;
    }
    pool_recycle_t *cur = p->recycle;
    //if(!cur) return 1;
    while(cur->next){
        if(cur->next->len < len)
            cur = cur->next;
        else
            break;
    }
    
    if(p->recycle == cur && cur->len > len){
        this->next = cur;
        p->recycle = this;
    } else {
        this->next = cur->next;
        cur->next = this;
    }

    return 0;
}

unsigned int pool_pfree(pool_t *pool,void *p){
    pool_large_t *large;

    if(!pool || !p) return 1;
        
    printf("pool_pfree p:%p pool->large:%p \n",p,pool->large);
    for(large = pool->large ; large ; large = large->next){
        if(large->alloc == p){
            printf("pool_pfree free p:%p  \n",p);
            free(large->alloc);
            large->alloc = NULL;
            return 0;
        }
    }

    return 1;
}

unsigned int pool_free(pool_t *pool,void *pdata,size_t len){
    if(pool_pfree(pool,pdata) == 1)
        return pool_recycle(pool,pdata,len);

    return 0;
}

pool_cleanup_t * pool_cleanup_add(pool_t *pool,size_t size){
    pool_cleanup_t *cln = pool_calloc(pool,sizeof(pool_cleanup_t));

    if(!cln) return NULL;

    if(size > 0){
        cln->data = pool_calloc(pool,size);
    }else {
        cln->data = NULL;
    }

    cln->handler = NULL;
    cln->next    = pool->cleanup;
    pool->cleanup = cln;

    return cln;
}


void pool_print(pool_t *pool){
    printf("--------begin print pool:%p\n",pool);
    if(!pool) return ;

    pool_t *p;   
    pool_large_t *large;
    pool_recycle_t *recycle;
    pool_cleanup_t   *cleanup; 

    for(p=pool; p ; p=p->d.next){
      //void *data_start = p+sizeof(pool_t);
      printf("pool(%p) maxsize:%ld used:%ld left:%ld last:%p  end:%p failed:%ld\n",
              p,p->max,(p->d.last - ((char*)p+sizeof(pool_t))),(p->d.end-p->d.last),p->d.last,p->d.end,p->d.failed);
      for(large = p->large ; large ; large = large->next){
         printf("pool(%p) p->large:%p\n",p,large);
      }
      for(recycle = p->recycle ; recycle ; recycle = recycle->next){
         printf("pool(%p) p->recycle:%p\n",p,recycle);
      }

      for(cleanup = p->cleanup ; cleanup ; cleanup = cleanup->next){
         printf("pool(%p) p->cleanup:%p data:%p\n",p,cleanup,cleanup->data);
      }
    }
    printf("--------end print pool:%p\n",pool);
}
